use unicode_segmentation::UnicodeSegmentation;

#[derive(Default)]
pub struct Row {
    string: String,
    len: usize
}

impl From<&str> for Row {
    fn from(s: &str) -> Self {
        let mut row = Self {
            string: String::from(s),
            len: 0
        };
        row.update_row();
        row
    }
}

impl Row {
    /*
     * update the length of the row.
     */
    pub fn update_row(&mut self) {
        self.len = self.string[..].graphemes(true).count();
    }

    /*
     * render the row from start index to end index and return the string.
     */
    pub fn render(&self, start: usize, end: usize) -> String {
        let end = std::cmp::min(end, self.string.len());
        let start = std::cmp::min(start, end);
        let mut result = String::new();
        for grapheme in self.string[..]
            .graphemes(true)
            .skip(start)
            .take(end - start) {
            //if got a tab, just replace it with a blank
            if grapheme == "\t" {
                // result.push_str(" ");
                result.push(' ');
            }else {
                result.push_str(grapheme);
            }
        }
        result
    }

    pub fn len(&self) -> usize {
        self.len
    }

    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    pub fn insert(&mut self, at: usize, c: char) {
        if at >= self.len() {
            self.string.push(c);
        }else {
            let mut result: String = self.string[..].graphemes(true).take(at).collect();
            let remainer: String = self.string[..].graphemes(true).skip(at).collect();
            result.push(c);
            result.push_str(&remainer);
            self.string = result;
        }
        self.update_row();
    }

    pub fn delete(&mut self, at: usize) {
        if at >= self.len() {
            return;
        }else {
            let mut result: String = self.string[..].graphemes(true).take(at).collect();
            let remainer: String = self.string[..].graphemes(true).skip(at + 1).collect();
            result.push_str(&remainer);
            self.string = result;
        }
        self.update_row();
    }

    pub fn append(&mut self, new: &Self) {
        // self.string.push_str(&new.string);
        self.string = format!("{}{}", self.string, new.string);
        self.update_row();
    }

    pub fn split(&mut self, at: usize) -> Self {
        let beginning: String = self.string[..].graphemes(true).take(at).collect();
        let remainder: String = self.string[..].graphemes(true).skip(at).collect();

        self.string = beginning;
        self.update_row();
        Row::from(&remainder[..])
    }

    pub fn as_bytes(&self) -> &[u8] {
        self.string.as_bytes()
    }

    // simple find which will encountered with other lauguages
    // pub fn find(&self, query: &str) -> Option<usize> {
    //     self.string.find(query)
    // }

    pub fn find(&self, query: &str) -> Option<usize> {
        let matching_byte_index = self.string.find(query);
        if let Some(matching_byte_index) = matching_byte_index {
            for (grapheme_index, (byte_index, _)) in 
                self.string[..].grapheme_indices(true).enumerate() 
            {
                if matching_byte_index == byte_index {
                    return Some(grapheme_index);
                }
            }
        }
        None
    }

}